package cn.gwm.flink.streaming.strategy.termal;

import cn.gwm.flink.streaming.beans.termalRunaway.BmsTrdStrategys;
import cn.gwm.flink.streaming.beans.termalRunaway.WaringTermalDwmWithStragegyEv;
import cn.gwm.flink.streaming.beans.termalRunaway.WaringTermalWithStragegyAndFlagEv;
import cn.gwm.flink.streaming.constant.DefaultConstant;
import cn.gwm.flink.streaming.constant.TermalComparisonTypeEnum;
import cn.gwm.flink.streaming.constant.TermalProcessResult;
import cn.gwm.flink.streaming.constant.TermalRunawayTypeEnum;
import cn.gwm.flink.streaming.function.process.TermalWarningRiseEvProcessFunction;
import cn.gwm.utils.StreamUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
import org.apache.flink.api.common.state.ListState;
import org.apache.flink.api.common.state.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.*;

/**
 * 热失控温升异常检测
 * 模组温度温升速率超过1度/3s
 * @author GW00283474
 */
public class TermalTemperatureRiseEvStrategy implements TermalStrategy{

    private static final Logger logger = LoggerFactory.getLogger(TermalTemperatureRiseEvStrategy.class);

    @Override
    public Dict check(State state, long timestamp) {
        try {
            ListState<WaringTermalWithStragegyAndFlagEv> listState=(ListState<WaringTermalWithStragegyAndFlagEv>)state;
            List<WaringTermalWithStragegyAndFlagEv> records = StreamUtil.convertIterable(listState.get());
            //如果状态值为null,则返回null
            if(CollectionUtil.isEmpty(records)){ return  null ;}
            records.sort(new Comparator<WaringTermalWithStragegyAndFlagEv>() {
                        @Override
                        public int compare(WaringTermalWithStragegyAndFlagEv o1, WaringTermalWithStragegyAndFlagEv o2) {
                            return (int) (o1.getItem_time()- o2.getItem_time());
                        }
                    });
            WaringTermalWithStragegyAndFlagEv last=records.get(0);
            WaringTermalWithStragegyAndFlagEv current=records.get(1);
            BmsTrdStrategys trdStrategy = current.getBmsTrdStrategies().stream().filter(s -> TermalRunawayTypeEnum.TEMPERATURERISE.getKey().equals(s.getItemType())).findFirst().get();
            Dict checkDict = tempRateRulePattern(last, current, trdStrategy.getComparisonType(), new BigDecimal(trdStrategy.getThreshold()), 30);
            current.setTemperatureRiseWarn(checkDict.get(TermalProcessResult.MATCH,false));
            checkDict.set(TermalProcessResult.CHECK_RECORD,current);
            List<WaringTermalDwmWithStragegyEv> check_records=new ArrayList<>(0);
            for(WaringTermalWithStragegyAndFlagEv s:records){
                WaringTermalDwmWithStragegyEv records_last=new WaringTermalDwmWithStragegyEv();
                BeanUtil.copyProperties(s,records_last);
                check_records.add(records_last);
            }
            checkDict.set(TermalProcessResult.CHECK_RECORDS,check_records);
            return checkDict;
        }catch (Exception e){
            e.printStackTrace();
        }
            return null;
    }

    /**
     * 模组温度温升速率超过1度/3s,且不低于30s上升10度
     */
    public static Dict tempRateRulePattern(WaringTermalWithStragegyAndFlagEv last, WaringTermalWithStragegyAndFlagEv current, int condition, BigDecimal threshold, Integer tempNum) throws Exception{
        Long duration = countDuration(last, current);
        Map<String,Double> rateMap=new HashMap<>();
        List<Integer> bmsBattModuleTemps=new ArrayList<>(0);
        //最近tid值
        long recentlyTid=Long.MIN_VALUE;
        recentlyTid=NumberUtil.max(last.getItem_time(),current.getItem_time(),recentlyTid);
        for (int i = 1; i <=tempNum ; i++) {
            int currentBmsBattModuleTemp=(int)current.getClass().getDeclaredField("BMS_BattModuleTemp"+i).get(current);
            int lastBmsBattModuleTemp=(int)last.getClass().getDeclaredField("BMS_BattModuleTemp"+i).get(last);
            //如果有缺省值，则该字段不参与计算
            if(DefaultConstant.BMS_BattModuleTemp.intValue()!=currentBmsBattModuleTemp && DefaultConstant.BMS_BattModuleTemp !=lastBmsBattModuleTemp){
                Double temp = NumberUtil.sub(currentBmsBattModuleTemp, lastBmsBattModuleTemp);
                Double rate = NumberUtil.div(temp, Convert.toDouble(duration));
                logger.info("EV计算温升速率rate+{},的两个属性值为current:{},last:{}",i,currentBmsBattModuleTemp,lastBmsBattModuleTemp);
                rateMap.put("rate"+i,rate);
                bmsBattModuleTemps.add(currentBmsBattModuleTemp);
                bmsBattModuleTemps.add(lastBmsBattModuleTemp);
            }

        }
        boolean match =false;
        if(CollectionUtil.isNotEmpty(rateMap)){

            logger.info("EV的vin:{}的温升速率计算结果集：{},currentItemTime:{},lastItemTime:{}",current.getDeviceId(), JSONUtil.toJsonPrettyStr(rateMap),current.getItem_time(),last.getItem_time());
            match = rateMap.values().stream().anyMatch(rate -> {
                boolean flag=false;
                if(TermalComparisonTypeEnum.EQ.getKey()==condition){
                    NumberUtil.equals(Convert.toBigDecimal(rate), threshold);
                }
                if(TermalComparisonTypeEnum.LE.getKey()==condition){
                    NumberUtil.isLessOrEqual(new BigDecimal(rate), threshold);
                }
                if(TermalComparisonTypeEnum.LESS.getKey()==condition){
                    flag= NumberUtil.isLess(new BigDecimal(rate), threshold);
                }
                if(TermalComparisonTypeEnum.GE.getKey()==condition){
                    flag=NumberUtil.isGreaterOrEqual(new BigDecimal(rate), threshold);
                }
                if(TermalComparisonTypeEnum.GREATER.getKey()==condition){
                    flag= NumberUtil.isGreater(new BigDecimal(rate), threshold);
                }
                if(TermalComparisonTypeEnum.EXCLUDE.getKey()==condition){
                    flag = !NumberUtil.equals(new BigDecimal(rate), threshold);
                }
                return flag;
            });
        }
        if(match){
            Integer[] arr=bmsBattModuleTemps.toArray(new Integer[bmsBattModuleTemps.size()]);
            int maxTemp=NumberUtil.max(arr);
            int minTemp=NumberUtil.min(arr);
            double maxRate=rateMap.values().stream().max(Comparator.comparingDouble(Double::doubleValue)).get();
            return Dict.of(TermalProcessResult.MATCH,true,
                    TermalProcessResult.MAX_TEMPERATURE,maxTemp,
                    TermalProcessResult.MIN_TEMPERATURE,minTemp,
                    TermalProcessResult.MAX_TEMPRISE,maxRate,
                    TermalProcessResult.THRESHOLD_TEMPRISE,threshold,
                    TermalProcessResult.RECENTLY_TID,recentlyTid,
                    TermalProcessResult.STRATEGY_KEY,TermalRunawayTypeEnum.TEMPERATURERISE.getKey(),
                    TermalProcessResult.STRATEGY_DESC,TermalRunawayTypeEnum.TEMPERATURERISE.getValue());
        }
        return Dict.of(TermalProcessResult.MATCH,false,
                TermalProcessResult.THRESHOLD_TEMPRISE,threshold,
                TermalProcessResult.RECENTLY_TID,recentlyTid,
                TermalProcessResult.STRATEGY_KEY,TermalRunawayTypeEnum.TEMPERATURERISE.getKey(),
                TermalProcessResult.STRATEGY_DESC,TermalRunawayTypeEnum.TEMPERATURERISE.getValue());
    }

    /**
     * 两条数据生成间隔
     *
     * @param last    上一条记录
     * @param current 当前记录
     */
    public static Long countDuration(WaringTermalWithStragegyAndFlagEv last, WaringTermalWithStragegyAndFlagEv current) {
        DateTime lastDateTime = DateUtil.date(Long.valueOf(last.getItem_time()));
        DateTime currentDateTime = DateUtil.date(Long.valueOf(current.getItem_time()));
        long duration = DateUtil.between(lastDateTime, currentDateTime, DateUnit.SECOND);
        if (duration == 0) {
            duration = 1;
        }
        return duration;
    }
}
